package com.qf.ability.sentinel.server.application;

import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
import com.alibaba.csp.sentinel.cluster.server.ClusterTokenServer;
import com.alibaba.csp.sentinel.cluster.server.SentinelDefaultTokenServer;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.qf.ability.sentinel.server.config.NacosDatasourceConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Properties;
import java.util.Set;

/**
 * description: 集群流控的运行类
 * author: Ken
 * 公众号：Java架构栈
 */
@Component
@Slf4j
public class SentinelServerRunning implements CommandLineRunner {

    //获取运行端口
    @Value("${server.port}")
    private int port;

    @Autowired
    private NacosDatasourceConfig nacosDatasourceConfig;

    @Override
    public void run(String... args) throws Exception {
        //启动Sentinel的集群流控管理服务
        // 加载服务配置
        ClusterServerConfigManager.loadGlobalTransportConfig(new ServerTransportConfig()
                .setIdleSeconds(600)
                .setPort(port));

        //启动服务
        ClusterTokenServer tokenServer = new SentinelDefaultTokenServer();
        tokenServer.start();
        log.debug("[sentine server running] 集群流控服务启动成功，port:{}", port);

        //配置为服务端
        ClusterStateManager.applyState(ClusterStateManager.CLUSTER_SERVER);

        //Nacos服务地址的配置对象
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosDatasourceConfig.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosDatasourceConfig.getNamespaces());

        //根据namespace 加载对应的集群限流规则
        ClusterFlowRuleManager.setPropertySupplier(namespace -> {
            log.debug("[sentinel server loader namespace] 加载到微服务[{}]的集群流控规则", namespace);
            ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(
                    properties,
                    nacosDatasourceConfig.getGroupId(),
                    namespace + "-flow-rule",
                    source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
            SentinelProperty<List<FlowRule>> property = ds.getProperty();
            return property;
        });

        //配置动态的命名空间数据源 -- 必须放在最后
        NacosDataSource<Set<String>> namespaceDs = new NacosDataSource<>(
                properties,
                nacosDatasourceConfig.getGroupId(),
                nacosDatasourceConfig.getNamespacedataId(),
                source -> JSON.parseObject(source, new TypeReference<Set<String>>() {}));
        //从Nacos上加载服务端需要管理的集群流控的所有服务的namespace
        ClusterServerConfigManager.registerNamespaceSetProperty(namespaceDs.getProperty());
    }
}
